#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>

#include <GL/gl.h>
#include <GL/glu.h>

#include "digmania.h"

render_context_t* create_render_context(client_options_t* opts)
{
	// allocating memory
	render_context_t *context = malloc(sizeof (render_context_t));
	if (!context) {
		fprintf(stderr, "[fatal] memory allocation problem\n");
		exit(1);
	}
	
	context->opts = opts;

	// initing sdl lib
	int ret = SDL_Init(SDL_INIT_VIDEO);
	if (ret < 0) {
		fprintf(stderr, "[fatal] sdl initialization failed: %s\n",
			SDL_GetError());
		exit(1);
	}

	const SDL_VideoInfo *video_info = SDL_GetVideoInfo();
	if (!video_info) {
		fprintf(stderr, "[fatal] video query failed: %s\n", SDL_GetError());
		exit(1);
	}

	// setting sdl/opengl video flags
	int flags = SDL_OPENGL; // enable opengl in sdl window
	flags |= SDL_GL_DOUBLEBUFFER; // use opengl double buffer
	flags |= SDL_HWPALETTE; // store pallete in hardware
	flags |= SDL_RESIZABLE; // make sdl window resizable

	// store surface in hardware if supported
	if (video_info->hw_available) flags |= SDL_HWSURFACE;
	else flags |= SDL_SWSURFACE;

	if (video_info->blit_hw) flags |= SDL_HWACCEL;

	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	context->video_flags = flags;

	SDL_Surface *surface = SDL_SetVideoMode(opts->window_width,
		opts->window_height, opts->color_depth, context->video_flags);

	if (!surface) {
		fprintf(stderr, "[fatal] failed to set video mode: %s\n",
			SDL_GetError());
		exit(1);
	}

	context->surface = surface;

	// Init opengl parameters

	init_gl_textures(context);
	/* Enable Texture Mapping */
	glEnable( GL_TEXTURE_2D );
	/* Enable smooth shading */
	glShadeModel( GL_SMOOTH );
	/* Set the background black */
	glClearColor( 0.0f, 0.0f, 0.0f, 0.5f );
	/* Depth buffer setup */
	glClearDepth( 1.0f );
	/* Enables Depth Testing */
	glEnable( GL_DEPTH_TEST );
	/* The Type Of Depth Test To Do */
	glDepthFunc( GL_LEQUAL );
	/* Really Nice Perspective Calculations */
	glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );

	SDL_WM_SetCaption("Digmania", 0);
	context->is_window_active = 1;
	context->state = CS_GAME;

	resize_window(context->opts->window_width, context->opts->window_height);

	return context;
}

void destroy_render_context(render_context_t *context)
{
	SDL_Quit();
	free(context);
}

void resize_window(int width, int height)
{
	if (height == 0) height = 1;
	GLfloat ratio = (GLfloat) width / (GLfloat) height;
	/* Setup our viewport. */
	glViewport(0, 0, (GLint) width, (GLint) height);
	/* change to the projection matrix and set
	 * our viewing volume. */
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	/* Set our perspective */
	gluPerspective(45.0f, ratio, 0.1f, 100.0f);
	/* Make sure we're chaning the model view and not the projection */
	glMatrixMode(GL_MODELVIEW);
	/* Reset The View */
	glLoadIdentity();
}

void handle_key(render_context_t* context, SDL_keysym *keysym)
{
	switch ( keysym->sym )
	{
		case SDLK_ESCAPE:
			context->work = 0;
			break;
	}
}

void dispatch_events(render_context_t *context)
{
	SDL_Event event;
	while (SDL_PollEvent(&event)) {
		switch(event.type) {
			case SDL_ACTIVEEVENT:
				context->is_window_active = event.active.gain;
				break;
			case SDL_VIDEORESIZE:
				context->surface = SDL_SetVideoMode(event.resize.w,
						event.resize.h,	context->opts->color_depth,
						context->video_flags);
				if (!context->surface) {
					fprintf( stderr, "[fatal] Surface crashed after resize: %s\n",
							SDL_GetError());
					context->is_window_active = 0;
					context->work = 0;
				} 
				else resize_window(event.resize.w, event.resize.h);
				break;
			case SDL_KEYDOWN:
				/* handle key presses */
				handle_key(context, &event.key.keysym);
				break;
			case SDL_QUIT:
				/* handle quit requests */
				context->work = 0;
				break;
		}
	}
}

void load_texture(GLuint texture, const char *path)
{
	SDL_Surface *image = SDL_LoadBMP(path);
	if (!image) {
		fprintf(stderr, "[error] Can not load texture %s.\n", path);
		return;
	}

	glBindTexture(GL_TEXTURE_2D, texture);

	glTexImage2D(GL_TEXTURE_2D, 0, 3, image->w, image->h, 0, 0x80E0,
			GL_UNSIGNED_BYTE, image->pixels );

	SDL_FreeSurface(image);
}

void init_gl_textures(render_context_t *context)
{
	glGenTextures(BT_COUNT, (GLuint*) &context->textures);

	load_texture(context->textures[BT_STONE], "data/blocks/stone.bmp");

	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
}

void draw_gl_scene(render_context_t *context)
{
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	glLoadIdentity();
	glTranslatef(0.0f, 0.0f, -5.0f);

	gluLookAt(2.f,2.f,2.f, 0.f,0.f,0.f, 0.f,1.f,0.f);

	glBindTexture(GL_TEXTURE_2D, context->textures[BT_STONE]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

	glBegin(GL_QUADS);
	/* Front Face */
	/* Bottom Left Of The Texture and Quad */
	glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -1.0f, -1.0f, 1.0f );
	/* Bottom Right Of The Texture and Quad */
	glTexCoord2f( 1.0f, 1.0f ); glVertex3f(  1.0f, -1.0f, 1.0f );
	/* Top Right Of The Texture and Quad */
	glTexCoord2f( 1.0f, 0.0f ); glVertex3f(  1.0f,  1.0f, 1.0f );
	/* Top Left Of The Texture and Quad */
	glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -1.0f,  1.0f, 1.0f );

	/* Back Face */
	/* Bottom Right Of The Texture and Quad */
	glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -1.0f, -1.0f, -1.0f );
	/* Top Right Of The Texture and Quad */
	glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -1.0f,  1.0f, -1.0f );
	/* Top Left Of The Texture and Quad */
	glTexCoord2f( 1.0f, 1.0f ); glVertex3f(  1.0f,  1.0f, -1.0f );
	/* Bottom Left Of The Texture and Quad */
	glTexCoord2f( 1.0f, 0.0f ); glVertex3f(  1.0f, -1.0f, -1.0f );

	/* Top Face */
	/* Top Left Of The Texture and Quad */
	glTexCoord2f( 1.0f, 1.0f ); glVertex3f( -1.0f,  1.0f, -1.0f );
	/* Bottom Left Of The Texture and Quad */
	glTexCoord2f( 1.0f, 0.0f ); glVertex3f( -1.0f,  1.0f,  1.0f );
	/* Bottom Right Of The Texture and Quad */
	glTexCoord2f( 0.0f, 0.0f ); glVertex3f(  1.0f,  1.0f,  1.0f );
	/* Top Right Of The Texture and Quad */
	glTexCoord2f( 0.0f, 1.0f ); glVertex3f(  1.0f,  1.0f, -1.0f );

	/* Bottom Face */
	/* Top Right Of The Texture and Quad */
	glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -1.0f, -1.0f, -1.0f );
	/* Top Left Of The Texture and Quad */
	glTexCoord2f( 1.0f, 1.0f ); glVertex3f(  1.0f, -1.0f, -1.0f );
	/* Bottom Left Of The Texture and Quad */
	glTexCoord2f( 1.0f, 0.0f ); glVertex3f(  1.0f, -1.0f,  1.0f );
	/* Bottom Right Of The Texture and Quad */
	glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -1.0f, -1.0f,  1.0f );

	/* Right face */
	/* Bottom Right Of The Texture and Quad */
	glTexCoord2f( 0.0f, 0.0f ); glVertex3f( 1.0f, -1.0f, -1.0f );
	/* Top Right Of The Texture and Quad */
	glTexCoord2f( 0.0f, 1.0f ); glVertex3f( 1.0f,  1.0f, -1.0f );
	/* Top Left Of The Texture and Quad */
	glTexCoord2f( 1.0f, 1.0f ); glVertex3f( 1.0f,  1.0f,  1.0f );
	/* Bottom Left Of The Texture and Quad */
	glTexCoord2f( 1.0f, 0.0f ); glVertex3f( 1.0f, -1.0f,  1.0f );

	/* Left Face */
	/* Bottom Left Of The Texture and Quad */
	glTexCoord2f( 1.0f, 0.0f ); glVertex3f( -1.0f, -1.0f, -1.0f );
	/* Bottom Right Of The Texture and Quad */
	glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -1.0f, -1.0f,  1.0f );
	/* Top Right Of The Texture and Quad */
	glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -1.0f,  1.0f,  1.0f );
	/* Top Left Of The Texture and Quad */
	glTexCoord2f( 1.0f, 1.0f ); glVertex3f( -1.0f,  1.0f, -1.0f );
	glEnd( );

	SDL_GL_SwapBuffers();
}

void render_loop(render_context_t* context)
{
	context->work = 1;

	while(context->work) {
		dispatch_events(context);
		//if (context->is_window_active) 
			draw_gl_scene(context);
	}
}

